import Evt from '../event'
import Algo from '../algo'
import Utils from '../utils'
import Shape from './shape'
import {CN,EN,Web,Action,Letter,Cst,PointCfg} from '../config'
import {ActionGroup} from '../config/action-auth'
export default class Mng extends Evt{
	constructor($env,$event,$version,$global){
		super()
		this.$env = $env
		this.$event = $event
		this.$version = $version

		this.tool = new Shape()
		this.active = false
		this.step = []
		this.ctrlIndex = 1
		this.ghostCvs = $env.get("ghostCvs")
		this.ghostCtx = this.ghostCvs.getContext("2d")
		this.pluginInstances = this.$env.get("pluginInstances")
		this.plugins=[]
		this.PointCfg = PointCfg
		this.ActionGroup = ActionGroup
		this.__t="'''''''''"
		this.__m="^"
		this.__bind()

		this.$global = $global
		for(let k in $global)
			this[k] = $global[k]
	}
	__bind(){
		;[
			Cst.init,
			Cst.focusIn,
			Cst.focusOut,
			EN.move,
			EN.up,
			EN.down,
			EN.click
			].forEach(en=>{
			let method = Utils.upperFLetter(en)
			this.on(en,(...a)=>{
				if(!this.name.match(/abstract/i)&&this[method])
					this[method](...a)
			})
		})

		;[
			EN.move,
			EN.up,
			EN.down,
			EN.click].forEach(en=>{
				this.$event.on(en,(...a)=>this.active&&this.emit(en,...a))
			})
		
	}
	focusIn(nav){
		console.log("focusIn",this.name,this.plugins)
		this.emit(Cst.focusIn,nav,this)
		this.active = true
		this.step=[]
		this.plugins.forEach((action)=>{
			this.$event.emit(EN.plugin,{action})
		})
	}
	focusOut(){
		console.log("focusOut",this.name)
		this.active = false
		this.step=[]
		this.clearRect()
		this.plugins.forEach(p=>{
			this.pluginInstances.has(p)&&this.pluginInstances.get(p).focusOut()
		})
		this.emit(Cst.focusOut,this)
	}
	clearRect(){
		let ctx = this.ghostCtx
		let {width,height} = ctx.canvas
		let {tx,ty} = this.$env.get("translate")
		ctx.save()
		ctx.translate(-tx,-ty)
		ctx.clearRect(0,0,width,height)
		ctx.restore()
	}
	
	getContextState(){
		let state = this.ghostCtx.getState()
		if(!this.$env.get("fillEnable"))
			state.fillStyle="transparent"
		state.merge(this.state)
		return state
	}
	inPath(...args){
		return Algo[this.name](...args)
	}
	generateLabel(){
		let action = this.name
		let letters = this.letters
		let index = this.$version.queryByAction(action).length;
		let m = index%letters.length
		let t = Math.floor(index/letters.length)
		return letters[m]+this.__t.slice(0,t)
	}
	//内部自我更新
	updatePath(stepData,dp,i){
		let step = stepData.step
		let {dx,dy} = dp
		let cache = step
		let hasCenter = ActionGroup.center.includes(stepData.action)
		if(i===0&&hasCenter)
			i=-1
		if(i!==-1){
			let p = step[i]
			cache = step.filter(p1=>Math.distance(p1,p)==0)
		}
		cache.forEach(p=>{
			p.x+=dx
			p.y+=dy
		})
		return stepData
	}
	// 关联更新
	updateRPath(steps){return steps}
	tip(){}
	beforeDrawFilter(stepData){
		return stepData
	}
	draw(stepData,context,focus){
		context = context||this.ghostCtx
		stepData = this.beforeDrawFilter(stepData.assign())
		let state = stepData.state
		let fs = state.fillStyle
		let ratio = state.lineWidth/2
		if(fs!=="transparent")
			state.fillStyle=fs+"33"
		if(context.ghost&&!focus)
			state.fillStyle=state.strokeStyle+"33"
		state.lineDash = state.lineDash.map(i=>i*ratio)
		this.tool.draw(stepData,context)
	}
	drawPoint(stepData,ctx){
		let {step,state} = stepData
		ctx.save()
		ctx.merge(state)
		step.forEach(p=>{
			if(p.ctrl&&!ctx.ghost)return
			ctx.save()
			this.tool.point(p,ctx)
			this.tool.drawLabel(p,ctx)
			ctx.restore()
		})
		ctx.restore()
	}
	//cvs transform pos 2 html pos
	ctxPox2html(pos){
		let {x,y} = pos
		let {tx,ty} = this.$env.get("translate")
		return {
			x:x+tx,
			y:y+ty
		}
	}
	togglePointer(flag){
		this.ghostCvs.classList[flag?"add":"remove"](CN.pointer)
	}
	loadMngByAction(action){
		let mng = this.$env.get("toolInstances").get(action)
		if(!mng)
			this.$event.emit(EN.paint,{action},true)
		return this.$env.get("toolInstances").get(action)
	}
	loadPluginByAction(action){
		return this.$env.get("pluginInstances").get(action)
	}
	toast(arg){
		let cfg={
			timeOut:1500
		}
		if(Object.isObject(arg))
			cfg.merge(arg)
		else
			cfg.text = arg
		this.$event.emit(EN.tip,cfg)
	}
	longToast(text){
		let cfg={
			timeOut:3000,
			text
		}
		this.$event.emit(EN.tip,cfg)
	}
	// {x,y,a}
	getLineMeta(){}
	// {x,y,r}
	getCircleMeta(){}
	getLineType(action){
		let ttype = ""
		switch(action){
			case Action.Line:
			case Action.Ray:
			case Action.CutLine:
			case Action.PointLine:
				ttype = Action.PointLine
				break
			default:
				ttype = Action.Linend
		}
		return ttype
	}
	pathFocus(step,ctx){
		ctx = ctx||this.ghostCtx
		let ss = step.state.strokeStyle
		let lw = step.state.lineWidth
		ctx.save()
		step = step.assign()
		step.state.merge({
			strokeStyle:ss,
			lineDash:[10,7,2,7],
			lineWidth:5,
			fillStyle:"transparent"
		})
		this.draw(step,ctx,true)
		ctx.restore()
	}
	getLM(path,pos){
		return this.loadMngByAction(path.action).getLineMeta(path,pos)
	}
	hl(line,pos,ctx){
		ctx = ctx||this.ghostCtx
		let meta = this.getLM(line,pos)
		if(!meta)return
		if(meta.sp)
			this.hlinend([meta,meta.sp],line.state,ctx)
		else 
			this.hline(meta,line.state,ctx)
	}
	hpath(path){
		this.loadMngByAction(path.action).pathFocus(path)
	}
	hpoint(p){
		this.tool.point(p,this.ghostCtx)
	}
	hline(p,state,ctx){
		this.hlineType(p,"line",state,ctx)
	}
	//ps=[p,p]
	hlinend(ps,state,ctx){
		this.hlineType(ps,"linend",state,ctx)
	}
	hlineType(ps,type,state,ctx){
		ctx = ctx||this.ghostCtx
		ctx.save()
		ctx.merge(state)
		ctx.merge({
			lineDash:[10,7,2,7],
			strokeStyle:ctx.strokeStyle,
			lineWidth:5
		})
		this.tool[type](ps,ctx)
		ctx.restore()
	}
	queryLines(pos){
		return this.$version.queryByActionAndPos(this.ActionGroup.lines,pos)[0]
	}
	queryMirrors(pos){
		return this.$version.queryByActionAndPos(this.ActionGroup.mirrors,pos)[0]
	}
	queryCircles(pos){
		return this.$version.queryByActionAndPos(this.ActionGroup.circles,pos)[0]
	}
	queryPolys(pos){
		return this.$version.queryByActionAndPos([Action.Polygon],pos)[0]
	}
	getRotateMeta(stepData){return stepData}
	mixin(mixs){
		mixs.forEach(mix=>{
			this.merge(mix)
		})
	}
	newPoint(pos,idx=0){
		let np = PointCfg.assign(pos)
		if(!np.text){
			let ls = Letter.capital
			let ps = this.$version.pointsSize+idx
			let l = ls.length
			let i = ps%l
			let t = Math.floor(i/l)
			np.text = ls[i]+this.__t.slice(0,t)
		}
		if(this.mirror)
			np.text+=this.__m
		np.idx=idx
		return np
	}
	refresh(pos){
		let state = this.getContextState()
		let steps = this.getPoints(this.step)
			pos&&steps.push(pos)
		this.clearRect()
		this.draw({
			step:steps,
			state
		})
	}
	//代理version commit
	commit(action,rtype,state){
		let _state = state||this.getContextState()
		action = action||this.name
		this.clearRect()
		this.$version.commit(action,{
			step:this.getPoints(this.step),
			state:_state,
			rtype
		})
		this.step=[]
	}
	//代理shape
	point(p,i){
		p = i===false?p:this.newPoint(p,i)
		this.tool.point(p,this.ghostCtx)
	}
	getPoints(ps){
		let ci = this.ctrlIndex
		return ps.map((p,i)=>{
			p.ctrl=ci!==-1&&i>=ci
			return this.newPoint(p,i)
		})
	}
}